﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.CodeDom;
using CodeBuilder2.Model.Enumeration;
using CodeBuilder2.BLL.Convertor;
using System.CodeDom.Compiler;
using System.IO;

namespace CodeBuilder2.BLL.CodeGenerateor
{
    class EntityGenerator
    {
        public string NameSpace { get; set; }
        public string ClassName { get; set; }
        public List<string> FieldNameList { get; set; }
        public DataTable FieldTableInfo { get; set; }

        public void GenerateCode()
        {

        }

        /// <summary>
        /// 获取为指定的代码文档对象模型编译单元生成代码
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="compileUnit"></param>
        /// <returns></returns>
        public string GenerateCode(CodeDomProvider provider)
        {
            CodeCompileUnit compileUnit = this.BuildEntityClassGraph();
            StringBuilder sb;
            using (StringWriter sw = new StringWriter(new StringBuilder()))
            {
                using (IndentedTextWriter tw = new IndentedTextWriter(sw, "    "))
                {
                    CodeGeneratorOptions cgo = new CodeGeneratorOptions();
                    cgo.ElseOnClosing = false;
                    // 默认是 Block，表示起始大括号和声明语句在同一行开始
                    cgo.BracingStyle = "C";
                    provider.GenerateCodeFromCompileUnit(compileUnit, tw, cgo);
                }
                sb = sw.GetStringBuilder();
            }
            System.Diagnostics.Debug.Write(sb.ToString());

            return sb.ToString();
        }

        // Build entity type class program graph using
        // System.CodeDom types.
        private CodeCompileUnit BuildEntityClassGraph()
        {
            // Create a new CodeCompileUnit to contain
            // the program graph.
            CodeCompileUnit ccu = new CodeCompileUnit();

            // Declare a new namespace called the property "NameSpace"'s value
            CodeNamespace nsSection = new CodeNamespace(NameSpace);

            // Add the new namespace to the compile unit.
            ccu.Namespaces.Add(nsSection);
            // Add some new namespace import.
            // For example : using System; using System.Collections.Generic;using System.Text;
            nsSection.Imports.Add(new CodeNamespaceImport("System"));
            nsSection.Imports.AddRange(new CodeNamespaceImport[]{
                new CodeNamespaceImport("System.Collections.Generic"),
                new CodeNamespaceImport("System.Linq"),
                new CodeNamespaceImport("System.Text"),
                // 即使写了下面这句话，最终生成代码时，日期类型的数据的变量类型依然
                // 是 "System.DateTime" 而不是 "DateTime"
                //new CodeNamespaceImport("System.DateTime")
            });

            // Declare a new type called the property "ClassName"'s value
            CodeTypeDeclaration classSection = new CodeTypeDeclaration(ClassName);
            // Add the new type to the namespace's type collection
            nsSection.Types.Add(classSection);

            // Declare some new field and their property
            var rows = from rc in FieldTableInfo.AsEnumerable()
                       select rc;
            string dataType = null;
            string fieldName = null;
            //string propertyName=null;
            Type fieldType = null;
            CodeMemberField cmf;
            CodeMemberProperty cmp;

            int dtIndex = FieldTableInfo.Columns.IndexOf("data_type");
            int cnIndex = FieldTableInfo.Columns.IndexOf("column_name");
            foreach (var row in rows)
            {
                dataType = row[dtIndex].ToString();
                fieldName = row[cnIndex].ToString().Substring(0, 1).ToLower() + row[cnIndex].ToString().Substring(1); ;
                fieldType = DataTypeConvertor.GetDataTypeName(dataType);
                cmf = new CodeMemberField(fieldType, fieldName);

                // Assign for variable cmp
                cmp = new CodeMemberProperty();
                cmp.Name = fieldName.Substring(0, 1).ToUpper() + fieldName.Substring(1);
                cmp.Attributes = (cmp.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
                //cmp.HasGet = true;
                //cmp.HasSet = true;
                cmp.Type = new CodeTypeReference(fieldType);
                cmp.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
                cmp.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName),
                    new CodePropertySetValueReferenceExpression()));

                // Add new field and new property to the type's member collection
                classSection.Members.Add(cmf);
                classSection.Members.Add(cmp);
            }
            return ccu;
        }
    }
}